#!/usr/bin/python
# -*- coding:utf-8 -*-
import math

import pandas as pd

from WellTrajectoryData import WellTrajectoryData
from decimal import Decimal, getcontext, ROUND_HALF_UP

# 设置全局舍入规则为四舍五入，保留两位小数
getcontext().rounding = ROUND_HALF_UP
DECIMAL_PRECISION = Decimal('0.00')  # 两位小数精度


class EstimateEigenvalues:
    def __init__(self, trajectory_data_pair):
        self.trajectory_data_pair = trajectory_data_pair
        self.calculated_trajectory_data = WellTrajectoryData()

    def estimate_eigenvalues(self, target_well_depth):
        max_data, min_data = self.trajectory_data_pair  # 假设 trajectory_data_pair 是 (maxData, minData) 元组

        if max_data.well_depth == target_well_depth:
            self.calculated_trajectory_data = max_data
        else:
            self.calculated_trajectory_data.well_slope = (min_data.well_slope - max_data.well_slope) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                     target_well_depth - max_data.well_depth) + max_data.well_slope
            self.calculated_trajectory_data.azimuth = (min_data.azimuth - max_data.azimuth) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                  target_well_depth - max_data.well_depth) + max_data.azimuth
            self.calculated_trajectory_data.vertical_depth = (min_data.vertical_depth - max_data.vertical_depth) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                         target_well_depth - max_data.well_depth) + max_data.vertical_depth
            self.calculated_trajectory_data.ns_coordinates = (min_data.ns_coordinates - max_data.ns_coordinates) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                         target_well_depth - max_data.well_depth) + max_data.ns_coordinates
            self.calculated_trajectory_data.ew_coordinates = (min_data.ew_coordinates - max_data.ew_coordinates) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                         target_well_depth - max_data.well_depth) + max_data.ew_coordinates
            self.calculated_trajectory_data.closed_orientation = (
                                                                             min_data.closed_orientation - max_data.closed_orientation) / (
                                                                             min_data.well_depth - max_data.well_depth) * (
                                                                             target_well_depth - max_data.well_depth) + max_data.closed_orientation
            self.calculated_trajectory_data.closed_displacement = (
                                                                              min_data.closed_displacement - max_data.closed_displacement) / (
                                                                              min_data.well_depth - max_data.well_depth) * (
                                                                              target_well_depth - max_data.well_depth) + max_data.closed_displacement
            self.calculated_trajectory_data.eye_displacement = (
                                                                           min_data.eye_displacement - max_data.eye_displacement) / (
                                                                           min_data.well_depth - max_data.well_depth) * (
                                                                           target_well_depth - max_data.well_depth) + max_data.eye_displacement
            self.calculated_trajectory_data.dogleg_degree = (min_data.dogleg_degree - max_data.dogleg_degree) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                        target_well_depth - max_data.well_depth) + max_data.dogleg_degree
            self.calculated_trajectory_data.ordinate = (min_data.ordinate - max_data.ordinate) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                   target_well_depth - max_data.well_depth) + max_data.ordinate
            self.calculated_trajectory_data.abscissa = (min_data.abscissa - max_data.abscissa) / (
                        min_data.well_depth - max_data.well_depth) * (
                                                                   target_well_depth - max_data.well_depth) + max_data.abscissa
            self.calculated_trajectory_data.well_depth = target_well_depth

        print(
            f"estimate_eigenvalues: target_well_depth={target_well_depth}, result={self.calculated_trajectory_data}\n")  # 调试信息
        return self.calculated_trajectory_data


class OtherParameters:
    # 计算最小曲率
    def calculate_min_curvature(self, epsilon):
        try:
            # 当 epsilon 不为 0 时，正常计算
            if epsilon != 0:
                result = 2 / epsilon * math.tan(epsilon / 2)
                return result
            else:
                # 当 epsilon 为 0 时，根据极限值返回 1
                return 1
        except Exception as e:
            # 捕获其他可能的异常，例如 math 域错误等
            print(f"Error in calculation: {e}")
            return 1  # 返回默认值 1

    def calculate_d_theta(self, ns_coord, ew_coord):
        if ns_coord == 0:
            # 如果 ns_coord 为 0，直接返回 PI/2
            return math.pi / 2
        try:
            # 计算反正切值的绝对值
            theta = abs(math.atan(ew_coord / ns_coord))
            return theta
        except Exception as e:
            # 捕获其他可能的异常，例如 math 域错误等
            print(f"Error in calculation: {e}")
            return math.pi / 2  # 返回默认值 PI/2

    def calculate_theta(self, ns_coord, ew_coord):
        if ns_coord > 0 and ew_coord > 0:
            return self.calculate_d_theta(ns_coord, ew_coord)
        elif ns_coord < 0 and ew_coord > 0:
            return math.pi - self.calculate_d_theta(ns_coord, ew_coord)
        elif ns_coord < 0 and ew_coord < 0:
            return math.pi + self.calculate_d_theta(ns_coord, ew_coord)
        else:
            return 2 * math.pi - self.calculate_d_theta(ns_coord, ew_coord)

    # def calculate_theta(self, ns_coord, ew_coord):
    #     if ns_coord == 0:
    #         return math.pi / 2 if ew_coord > 0 else 3 * math.pi / 2
    #
    #     basic_angle = self.calculate_d_theta(ns_coord, ew_coord)
    #     if ns_coord > 0 and ew_coord >= 0:
    #         return basic_angle
    #     elif ns_coord < 0 and ew_coord > 0:
    #         return math.pi - basic_angle
    #     elif ns_coord < 0 and ew_coord <= 0:
    #         return math.pi + basic_angle
    #     else:
    #         return 2 * math.pi - basic_angle
    def calculate_closed_direction(self, theta):
        try:
            # res= theta/math.pi*180
            # return res
            return math.degrees(theta)
        except Exception as e:
            # 捕获其他可能的异常，例如 math 域错误等
            print(f"Error in calculation: {e}")
            return 0

    def calculate_other_parameters(self, cur_prev_data_pair, design_direction=188.00, vertical_base_val=3246136.98,
                                   horrizontal_base_val=18550536.42):

        current_data = cur_prev_data_pair[1]
        previous_data = cur_prev_data_pair[0]
        if (current_data.well_depth > 0):
            paragraph = current_data.well_depth - previous_data.well_depth
            curv1 = previous_data.well_slope * math.pi / 180
            curv2 = current_data.well_slope * math.pi / 180
            derection_cur1 = previous_data.azimuth * math.pi / 180
            derection_cur2 = current_data.azimuth * math.pi / 180
            ε = math.cos(curv2 - curv1) - math.sin(curv2) * math.sin(curv1) * (
                        1 - math.cos(derection_cur2 - derection_cur1))
            min_curvature = self.calculate_min_curvature(math.acos(ε))
            current_data.vertical_depth = previous_data.vertical_depth + paragraph / 2 * (
                        math.cos(curv1) + math.cos(curv2)) * min_curvature
            current_data.ns_coordinates = previous_data.ns_coordinates + paragraph / 2 * (
                        math.sin(curv1) * math.cos(derection_cur1) + math.sin(curv2) * math.cos(
                    derection_cur2)) * min_curvature
            current_data.ew_coordinates = previous_data.ew_coordinates + paragraph / 2 * (
                        math.sin(curv1) * math.sin(derection_cur1) + math.sin(curv2) * math.sin(
                    derection_cur2)) * min_curvature
            cur_theta = self.calculate_theta(current_data.ns_coordinates, current_data.ew_coordinates)
            current_data.closed_orientation = self.calculate_closed_direction(cur_theta)
            current_data.closed_displacement = math.sqrt(
                current_data.ns_coordinates ** 2 + current_data.ew_coordinates ** 2)
            current_data.eye_displacement = math.cos(design_direction * math.pi / 180 - cur_theta) * math.sqrt(
                current_data.ns_coordinates ** 2 + current_data.ew_coordinates ** 2)
            current_data.dogleg_degree = math.acos(ε) * 180 / math.pi * 30 / paragraph
            current_data.ordinate = vertical_base_val + current_data.ns_coordinates
            current_data.abscissa = horrizontal_base_val + current_data.ew_coordinates
        return current_data


class TrueThickness:
    def calculate_true_thickness(self, upper_data, lower_data, inclination_angle, is_inclined):
        # 备注 is_inclined为True是上倾模式，为False是下倾模式
        if is_inclined:
            return ((lower_data.vertical_depth - upper_data.vertical_depth) + (
                        lower_data.eye_displacement - upper_data.eye_displacement) * (
                        math.tan(math.radians(inclination_angle)))) * (math.cos(math.radians(inclination_angle)))
        else:
            return ((lower_data.vertical_depth - upper_data.vertical_depth) - (
                        lower_data.eye_displacement - upper_data.eye_displacement) * (
                        math.tan(math.radians(inclination_angle)))) * (math.cos(math.radians(inclination_angle)))


class AngularDifference:
    def calculate_angular_difference(self, upper_slope, lower_slope, vertical_thickness, inclination_angle,
                                     is_inclined):
        if is_inclined:
            sin_value = math.sin(math.radians(((90 + inclination_angle) - (upper_slope + lower_slope) / 2)))
            oblique_depth = vertical_thickness / sin_value
            dogleg_degree = 30 * (lower_slope - upper_slope) / oblique_depth
            eye_displacement = math.cos(math.radians(abs(90 - (upper_slope + lower_slope) / 2))) * oblique_depth
            return oblique_depth, dogleg_degree, eye_displacement
        else:
            sin_value = math.sin(math.radians(((90 - inclination_angle) - (upper_slope + lower_slope) / 2)))
            oblique_depth = vertical_thickness / sin_value
            dogleg_degree = 30 * (lower_slope - upper_slope) / oblique_depth
            eye_displacement = math.cos(math.radians(abs(90 - (upper_slope + lower_slope) / 2))) * oblique_depth
            return oblique_depth, dogleg_degree, eye_displacement


class PointAPrediction:
    def predict_point_a(self, true_thickness, inclination_angle, vertical_depth, planar_distance, is_inclined):
        if is_inclined:
            return (vertical_depth + (true_thickness / math.cos(math.radians(inclination_angle))) - (
                        planar_distance * math.tan(math.radians(inclination_angle))))
        else:
            return (vertical_depth + (true_thickness / math.cos(math.radians(inclination_angle))) + (
                        planar_distance * math.tan(math.radians(inclination_angle))))


class TrueInclination:
    def calculate_true_inclination(self, vertical_depth, displacement, inclination_angle, is_inclined):
        if is_inclined:
            return (vertical_depth + displacement * math.tan(math.radians(inclination_angle))) * (
                math.cos(math.radians(inclination_angle)))
        else:
            return (vertical_depth - displacement * math.tan(math.radians(inclination_angle))) * (
                math.cos(math.radians(inclination_angle)))


class AngleCalculator:
    def calculate_phi_angle(self, current_angle, target_angle, formation_angle, is_upward):
        """计算Φ角度"""
        if is_upward:
            return abs(current_angle - target_angle) + formation_angle
        else:
            return abs(current_angle - target_angle) - formation_angle

    def calculate_sine_value(self, phi_angle):
        """计算正弦值"""
        return math.sin(math.radians(phi_angle))

    def calculate_oblique_depth(self, vertical_diff, phi_angle):
        """计算斜深"""
        sine_value = self.calculate_sine_value(phi_angle)
        if abs(sine_value) < 0.001:  # 避免除以零
            return vertical_diff
        return vertical_diff / sine_value

    def calculate_dogleg(self, current_angle, target_angle):
        """计算狗腿度"""
        return abs(current_angle - target_angle)

    def calculate_remaining_displacement(self, vertical_diff, phi_angle):
        """计算剩余视平移"""
        sine_value = self.calculate_sine_value(phi_angle)
        oblique_depth = self.calculate_oblique_depth(vertical_diff, phi_angle)
        return oblique_depth * sine_value

    def calculate_predicted_depth(self, true_thickness, formation_angle, current_depth, is_upward):
        """计算预测垂深"""
        if is_upward:
            return current_depth - true_thickness * math.cos(math.radians(formation_angle))
        else:
            return current_depth + true_thickness * math.cos(math.radians(formation_angle))

    def calculate_x_coordinate(self, plane_distance, formation_angle):
        """计算X坐标"""
        return plane_distance * math.cos(math.radians(formation_angle))

    def calculate_y_coordinate(self, plane_distance, formation_angle):
        """计算Y坐标"""
        return plane_distance * math.sin(math.radians(formation_angle))

    def calculate_remaining_plane_distance(self, plane_distance, formation_angle):
        """计算剩余平面距离"""
        x = self.calculate_x_coordinate(plane_distance, formation_angle)
        y = self.calculate_y_coordinate(plane_distance, formation_angle)
        return math.sqrt(x * x + y * y)
